package JET.geom;

import JET.physics.Mass;
import java.util.ArrayList;

/**
 * Represents general geometric 2D polygon.
 * The vertices may be added, the last vertiex closes polygon by connection to first one.
 * Polygon extends its size when new vertices are added.
 *
 * @author raos
 */
public final class Polygon {

    Vec2d[] verts;
    Vec2d[] edges;
    boolean valid = false;

    public Polygon() {
    }

    public Polygon(ArrayList<Mass> masses) {
        for(Mass m : masses)
            add(m.getPosition());
    }

    public void updateEdges() {
        int count = verts.length;
        edges = new Vec2d[count];
        
        for(int i=0; i<count-1; i++)
            edges[i] = verts[i+1].getSubbed(verts[i]);
        
        edges[count-1] = verts[0].getSubbed(verts[verts.length-1]);
    }

    public void add(Vec2d v) {
        if (verts == null) {
            verts = new Vec2d[1];
            verts[0] = new Vec2d(v);

            valid = false;
        } else {
            int count = verts.length;
            Vec2d[] newVerts = new Vec2d[count+1];

            for(int i=0; i<count; i++)
                newVerts[i] = verts[i];

            newVerts[count] = new Vec2d(v);
            verts = newVerts;
            if (verts.length>2) {
                valid = true;
                updateEdges();
            }
        }
    }

    public void clear() {
        verts = null;
        edges = null;
        valid = false;
    }

    public int getCount() {
        return verts.length;
    }

    public boolean isValid() {
        return valid;
    }

    public Vec2d getEdge(int index) throws IndexOutOfBoundsException {
        if (valid && index >=0 && index<verts.length )
            return edges[index];
        else
            throw new IndexOutOfBoundsException("Cannot get "+index+" edge of "+verts.length+"-vertices polygon!");
    }

    public Vec2d getVertex(int index) {
        return verts[index];
    }

    public boolean isConvex() throws RuntimeException {
        if (valid) {
            double z = Vec2d.crossZ(edges[edges.length-1], edges[0]);

            for(int i=0; i<edges.length-2; i++)
                if (z * Vec2d.crossZ(edges[i], edges[i+1]) < 0)
                    return false;
            
            return true;
        } else
            throw new RuntimeException("Cannot determine if not valid polygon is convex!");
    }

    public boolean isClockwise() throws RuntimeException {
        if (valid) {
            double z = Vec2d.crossZ(edges[edges.length-1], edges[0]);
            for(int i=0; i<edges.length-2; i++)
                z += Vec2d.crossZ(edges[i], edges[i+1]);

            return z<=0;
        } else
            throw new RuntimeException("Cannot determine if not valid polygon is clockwise!");
    }

    /**
     * Calculates area of this polygon.
     * Note that if this polygon is clockwise area value will be lower than zero!
     * 
     * @return Area of this polygon.
     * @throws RuntimeException
     */
    public double getArea() throws RuntimeException {
        if (valid) {
            double area = 0;
            
            for(int i=0; i<verts.length-1; i++)
                area += verts[i].x*verts[i+1].y - verts[i+1].x*verts[i].y;

            return area*.5;
        } else
            throw new RuntimeException("Cannot calculate area of non valid polygon!");
    }

    /**
     * Reverts polygon orientation.
     * First vertex stays at the same position.
     * 
     * @throws RuntimeException
     */
    public void reverse() throws RuntimeException {
        if (!valid)
            throw new RuntimeException("Cannot reverse not valid polygon!");

        int count = getCount();
        Vec2d[] newVerts = new Vec2d[count];

        newVerts[0] = verts[0];

        for(int i=1; i<count; i++)
            newVerts[i] = verts[count-i];

        verts = newVerts;
        updateEdges();
    }

    public void printDebugInfo() {
        System.out.println("isValid: "+isValid());
        System.out.println("isConvex: "+isConvex());
        System.out.println("isClockwise: "+isClockwise());
        System.out.println("getArea: "+getArea());

        System.out.println("edges: ");
        for(int i=0; i<getCount(); i++)
            System.out.println("edge["+i+"]: "+getEdge(i));
    }

    public static void main(String[] args) {
        Polygon p = new Polygon();

        p.add(new Vec2d(0, 0));
        p.add(new Vec2d(1, 0));
        p.add(new Vec2d(0, 1));
        
        p.printDebugInfo();
        p.reverse();
        p.printDebugInfo();
    }
}